package com.example.app;

import android.content.Context;
import android.net.Uri;
import android.util.Log;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;


public class WriteHandlingWebViewClient extends WebViewClient {

    private final String INTERCEPT_LOGIN_PATH = "/quick_login/v2/";

    private final String MIME_TYPE = "text/html";

    private final String MARKER = "AJAXINTERCEPT";

    private final Map<String, String> ajaxRequestContents = new HashMap<>();

    private final OkHttpClient client = new OkHttpClient();

    public WriteHandlingWebViewClient(WebView webView) {
        AjaxInterceptJavascriptInterface ajaxInterface = new AjaxInterceptJavascriptInterface(this);
        webView.addJavascriptInterface(ajaxInterface, "interception");
    }

    @Override
    public final WebResourceResponse shouldInterceptRequest(final WebView view, WebResourceRequest request) {
        String requestBody = null;
        Uri uri = request.getUrl();
        if (isAjaxRequest(request)) {
            requestBody = getRequestBody(request);
            uri = getOriginalRequestUri(request, MARKER);
        }
        WebResourceResponse webResourceResponse = shouldInterceptRequest(new WriteHandlingWebResourceRequest(request, requestBody, uri));
        if (webResourceResponse == null) {
            return null;
        } else if (INTERCEPT_LOGIN_PATH.equals(request.getUrl().getPath())) {
            return webResourceResponse;
        } else {
            return injectIntercept(webResourceResponse, view.getContext());
        }
    }

    /*
    This here is the "fixed" shouldInterceptRequest method that you should override.
    It receives a WriteHandlingWebResourceRequest instead of a WebResourceRequest.
     */
    public WebResourceResponse shouldInterceptRequest(WriteHandlingWebResourceRequest request) {
        if ("/".equals(request.getUrl().getPath())) {  // 拦截sso首页进行脚本注入
            Response response = sendRequest(new Request.Builder().url(request.getUrl().toString())
                    .headers(Headers.of(request.getRequestHeaders()))
                    .method(request.getMethod(), null)
                    .build());
            ResponseBody body = response.body();
            assert body != null;
            return new WebResourceResponse(MIME_TYPE, "gzip", body.byteStream());
        } else if (INTERCEPT_LOGIN_PATH.equals(request.getUrl().getPath())) {
            Response response = sendRequest(new Request.Builder().url(request.getUrl().toString())
                    .headers(Headers.of(request.getRequestHeaders()))
                    .method(request.getMethod(), RequestBody.create(request.getAjaxData().getBytes()))
                    .build());
            // 截取响应头部
            Headers headers = response.headers();
            Set<String> cookieSet = new LinkedHashSet<>();
            for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
                if ("Set-Cookie".equalsIgnoreCase(header.getKey())) {
                    cookieSet.addAll(header.getValue());
                }
            }
            // TODO 调用接口传递
            Log.i("代理链接", "AjaxData和Cookies信息需要保留，通过“接口A”传给后台：\n" +
                    request.getAjaxData() + "\n" + cookieSet);
            ResponseBody body = response.body();
            assert body != null;
            return new WebResourceResponse(MIME_TYPE, "gzip", body.byteStream());
        }
        return null;
    }

    private Response sendRequest(Request request) {
        try {
            return client.newCall(request).execute();
        } catch (IOException e) {
            Log.e("代理链接", "请求url->" + request.url(), e);
            throw new RuntimeException(e);
        }
    }

    void addAjaxRequest(String id, String body) {
        ajaxRequestContents.put(id, body);
    }

    private String getRequestBody(WebResourceRequest request) {
        String requestID = getAjaxRequestID(request);
        return getAjaxRequestBodyByID(requestID);
    }

    private boolean isAjaxRequest(WebResourceRequest request) {
        return request.getUrl().toString().contains(MARKER);
    }

    private String[] getUrlSegments(WebResourceRequest request, String divider) {
        String urlString = request.getUrl().toString();
        return urlString.split(divider);
    }

    private String getAjaxRequestID(WebResourceRequest request) {
        return getUrlSegments(request, MARKER)[1];
    }

    private Uri getOriginalRequestUri(WebResourceRequest request, String marker) {
        Log.i("代理链接", "old url ->" + request.getUrl().toString());
        String urlString = getUrlSegments(request, marker)[0];
        Log.i("代理链接", "new url ->" + urlString);
        return Uri.parse(urlString);
    }

    private String getAjaxRequestBodyByID(String requestID) {
        String body = ajaxRequestContents.get(requestID);
        ajaxRequestContents.remove(requestID);
        return body;
    }

    private WebResourceResponse injectIntercept(WebResourceResponse response, Context context) {
        String encoding = response.getEncoding();
        String mime = response.getMimeType();
        //  WebResourceResponse的mime必须为"text/html",不能是"text/html; charset=utf-8"
        if (mime.contains("text/html")) {
            mime = "text/html";
        }
        InputStream responseData = response.getData();
        InputStream injectedResponseData = injectInterceptToStream(context, responseData, mime, encoding);
        return new WebResourceResponse(mime, encoding, injectedResponseData);
    }

    private InputStream injectInterceptToStream(Context context, InputStream is, String mime, String charset) {
        try {
            byte[] pageContents = Utils.consumeInputStream(is);
            if (mime.equals(MIME_TYPE)) {
                String pageText = AjaxInterceptJavascriptInterface.enableIntercept(context, pageContents);
                // TODO 待验证
                //                pageContents = pageText.getBytes(charset);
                pageContents = pageText.getBytes(StandardCharsets.UTF_8);
            }
            return new ByteArrayInputStream(pageContents);
        } catch (Exception e) {
            Log.e("请求代理", "异常：" + e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
    }

}